デバッグのために毎フレーム出てくる情報を見やすく整備する < rusterizer-from-dots
from gltfクレートによる3Dモデルの読み込み(とバグ取り) < rusterizer-from-dots
デバッグのシステムを作る < rusterizer-from-dots
1. ワンループの中で条件式が満たされた回数をカウントする
2. 画面に毎フレーム情報を表示する
1. ワンループの中で条件式が満たされた回数をカウントする
名前を表示しつつtracking出来ると嬉しいねというやつappbird.icon
code:debug/inloop_counter.rs
pub struct InLoopCounter<'a> {
name:&'a str,
positive:usize,
negative:usize
}
impl<'a> InLoopCounter<'a> {
pub fn new(name: &'a str) -> Self {
InLoopCounter { name, positive: 0, negative: 0 }
}
pub fn count(&mut self, s:bool){
if s { self.positive += 1; } else { self.negative += 1; }
}
pub fn reset(&mut self) {
self.positive = 0;
self.negative = 0;
}
pub fn describe(&self) -> String {
return format!("{}: {} / {}", self.name, self.positive, self.negative);
}
}
ほい。appbird.icon
この上で
code:rs
pub static mut triangle_counter:InLoopCounter = InLoopCounter::new("triangle_counter");
....エラーになる!なんで?
staticなのはconstantにしましょうね、とのことらしい
std::sync::LazyLock::newを使えとのことappbird.icon
どうやらstatic変数は型アノテーションが必須らしい
code:rs
pub static mut TRIANGLE_COUNTER:LazyLock<InLoopCounter> = std::sync::LazyLock::new(|| InLoopCounter::new("triangle_counter"));
試しにテストしてみよう
げ、unsafeなのかこの操作
code:main.rs
TRIANGLE_COUNTER.count(1 == 2);
//use of mutable static is unsafe and requires unsafe function or block
// mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
まぁ、そうですよね...いくつものスレッドからこれを触ることを想定すれば、まぁそうなる
Arcとかが必要?
metricsクレートとかいろいろあるらしいが....
cortex_mクレートなるものもあるらしい
https://tomoyuki-nakabayashi.github.io/book/peripherals/singletons.html
std::sync::atomic::AtomicUsizeとかあるらしい
今んとこの要求を満たしているのはこれだが、今後デバッグによっては拡張する可能性が高い
once_cellクレートを使う手立てもあるらしい
Once_cellクレートによる解決が一番シンプルかな
ん、stdに入ってるじゃん
https://doc.rust-lang.org/std/cell/struct.OnceCell.html
Arc<Mutex<T>>を使う手立てもあるらしい
一番依存が少なく済むのはこいつだがうまく動くのか?
std::sync::LazyLock::newを使えとのことappbird.icon
ユーザーがわざわざ初期化タイミングを指定する必要がなくて、最初にアクセスしたときに初期化されていればいい場合があります。その時にはstd::cell::LazyCellがあります。これは内部的にOnceCellを抱えて、値がなかったら握っている関数で勝手に初期化するという仕組みです
あぁ、そういう仕組みなのか...
まぁ....じゃあこうなるのね、理解
code:rs
pub static TRIANGLE_COUNTER: LazyLock<Arc<Mutex<InLoopCounter>>> =
LazyLock::new(||
Arc::new(
Mutex::new(InLoopCounter::new("triangle_counter"))
)
);
長い!!マクロを使う
code:rs
macro_rules! def_inloop_counter {
($name:ident) => {
pub static $name: LazyLock<Arc<Mutex<InLoopCounter>>> =
LazyLock::new(||
Arc::new(
Mutex::new(InLoopCounter::new(stringify!($name)))
)
);
}
}
def_inloop_counter!(TRIANGLE_COUNTER);
というわけで、例えばこんな感じに使える!
code:rs
while canvas.update()? {
let t = canvas.passed_time();
:
TRIANGLE_COUNTER.lock().unwrap().count(1 == 1);
TRIANGLE_COUNTER.lock().unwrap().count(1 == 2);
world.update(canvas.deltatime());
world.draw(&camera, &mut canvas);
println!("{}", TRIANGLE_COUNTER.lock().unwrap().describe());
TRIANGLE_COUNTER.lock().unwrap().reset();
}
code:cmd
TRIANGLE_COUNTER: 1 / 2
2. 画面に毎フレーム情報を表示する
本当はフォントを表示する仕組みまで実装したかったが...
今回の関心はラスタライザーの理解にあり、フォント表示そのものには関心がそれほどないのでいったんカンニングしてしまうことにする。
ttfフォントなら二次ベジェ曲線のレンダリングが必要となるだろうappbird.icon
Coding Adventure: Rendering Text
まぁこれで一つのトピックの動画が70分ぐらいにわたって展開されている。
多分ラスタライザを作ることから脱線してしまいそうなので、また今度。
と、いうわけで、minifbのaddonとして実装されたフォント描画クレートを使おう。
https://crates.io/crates/minifb_fonts
どこに実装しようか...?appbird.icon
一番楽なこと言うならCanvasなんだが、デバッグ用の機能を混ぜたくはないという気持ちもある
外部オブジェクトがCanvasの参照を持ってうんたら描くという手もなくはないが...appbird.icon
うーん、思ったより面倒くさそう
...本当に欲しいのは画面に毎フレーム表示する機能か?
コンソールに毎フレーム出力しなおしてくれる機能さえあればいいんじゃないかという気がするappbird.icon
こちらも、flushするまでに出力した行数を覚えておいて、フレームでリセットするときに全部巻き戻してくれるやつを作っておいた。
そこらの制御にはcrosstermを使っておく。
code:rs
use std::{io::{stdout, Write}, sync::{Arc, LazyLock, Mutex}};
use crossterm::{QueueableCommand, cursor};
pub struct PrintOnLoop {
lines:u16
}
impl PrintOnLoop {
fn new() -> Self {
Self { lines: 0 }
}
pub fn println(&mut self, msg:&str) -> std::io::Result<()> {
stdout().write(msg.as_bytes())?;
stdout().write("\n".as_bytes())?;
self.lines += 1;
Ok(())
}
pub fn flush(&mut self) -> std::io::Result<()> {
stdout().flush()?;
stdout().queue(cursor::MoveToPreviousLine(self.lines))?;
self.lines = 0;
Ok(())
}
}
どの場所からでも出力できるように、こちらもSingletonパターンをとる。
code:rs
pub static LOGGER_ON_LOOP: LazyLock<Arc<Mutex<PrintOnLoop>>> =
LazyLock::new(||
Arc::new(
Mutex::new(PrintOnLoop::new())
)
);